﻿using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using UIOC.CodeAnalysis.Extensions;
using UIOC.CodeAnalysis.Helpers;

namespace UIOC.CodeAnalysis.Suppressors;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class FileFieldSuppressor : DiagnosticSuppressor {

    private static void LogInfo(string message) {
        SourceOutputHelper.LogInfo(DiagnosticSeverity.Info, $"[{nameof(FileFieldSuppressor)}] {message}");
    }

    private static readonly SuppressionDescriptor SuppressCS0612 = new(
        id: "SUPPRESS_CS0612",
        suppressedDiagnosticId: "CS0612",
        justification: "[Obsolete] 可用");

    public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions =>
       ImmutableArray.Create(SuppressCS0612);

    public override void ReportSuppressions(SuppressionAnalysisContext context) {
        foreach (var diagnostic in context.ReportedDiagnostics) {

            if (diagnostic.Id != SuppressCS0612.SuppressedDiagnosticId)
                continue;

            var compilation = context.Compilation;

            var internalAttributeSymbol = compilation.GetTypeByMetadataName(AttributeFullNames.Internal);
            if (internalAttributeSymbol == null) {
                return;
            }

            var location = diagnostic.Location;
            if (location == Location.None) {
                continue;
            }

            var syntaxTree = location.SourceTree;
            if (syntaxTree == null) {
                continue;
            }

            var root = syntaxTree.GetRoot(context.CancellationToken);
            var token = root.FindToken(location.SourceSpan.Start);
            var node = token.Parent.AncestorsAndSelf()
                .OfType<ExpressionSyntax>()
                .FirstOrDefault();

            if (node == null) {
                continue;
            }

#pragma warning disable RS1030 // 请勿在诊断分析器中调用 Compilation.GetSemanticModel() 方法
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
#pragma warning restore RS1030 // 请勿在诊断分析器中调用 Compilation.GetSemanticModel() 方法
            var accessedSymbol = semanticModel.GetSymbolInfo(node, context.CancellationToken).Symbol;

            if (accessedSymbol == null) {
                continue;
            }

            if (accessedSymbol.Kind is not SymbolKind.Field and not SymbolKind.Property and not SymbolKind.Method) {
                continue;
            }

            bool isMarkedWithInternal = accessedSymbol.GetAttributes(internalAttributeSymbol).Any();
            if (isMarkedWithInternal) {
                context.ReportSuppression(Suppression.Create(
                    SuppressCS0612,
                    diagnostic));
            }
        }
    }
}
